home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Blender 2.49b / blender-2.49b-windows.exe / $_4_ / .blender / scripts / bpymodules / dxfReader.py < prev    next >
Text File  |  2009-08-31  |  12KB  |  382 lines

  1. """This module provides a function for reading dxf files and parsing them into a useful tree of objects and data.
  2.  
  3.     The convert function is called by the readDXF fuction to convert dxf strings into the correct data based
  4.     on their type code.  readDXF expects a (full path) file name as input.
  5. """
  6.  
  7. # --------------------------------------------------------------------------
  8. # DXF Reader v0.9 by Ed Blake (AKA Kitsu)
  9. #  2008.05.08 modif.def convert() by Remigiusz Fiedler (AKA migius)
  10. # --------------------------------------------------------------------------
  11. # ***** BEGIN GPL LICENSE BLOCK *****
  12. #
  13. # This program is free software; you can redistribute it and/or
  14. # modify it under the terms of the GNU General Public License
  15. # as published by the Free Software Foundation; either version 2
  16. # of the License, or (at your option) any later version.
  17. #
  18. # This program is distributed in the hope that it will be useful,
  19. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21. # GNU General Public License for more details.
  22. #
  23. # You should have received a copy of the GNU General Public License
  24. # along with this program; if not, write to the Free Software Foundation,
  25. # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  26. #
  27. # ***** END GPL LICENCE BLOCK *****
  28. # --------------------------------------------------------------------------
  29.  
  30.  
  31. #from dxfImportObjects import *
  32.  
  33. class Object:
  34.     """Empty container class for dxf objects"""
  35.  
  36.     def __init__(self, _type='', block=False):
  37.         """_type expects a string value."""
  38.         self.type = _type
  39.         self.name = ''
  40.         self.data = []
  41.  
  42.     def __str__(self):
  43.         if self.name:
  44.             return self.name
  45.         else:
  46.             return self.type
  47.  
  48.     def __repr__(self):
  49.         return str(self.data)
  50.  
  51.     def get_type(self, kind=''):
  52.         """Despite the name, this method actually returns all objects of type 'kind' from self.data."""
  53.         if type:
  54.             objects = []
  55.             for item in self.data:
  56.                 if type(item) != list and item.type == kind:
  57.                     # we want this type of object
  58.                     objects.append(item)
  59.                 elif type(item) == list and item[0] == kind:
  60.                     # we want this type of data
  61.                     objects.append(item[1])
  62.             return objects
  63.  
  64.  
  65. class InitializationError(Exception): pass
  66.  
  67. class StateMachine:
  68.     """(finite) State Machine from the great David Mertz's great Charming Python article."""
  69.  
  70.     def __init__(self):
  71.         self.handlers = []
  72.         self.startState = None
  73.         self.endStates = []
  74.  
  75.     def add_state(self, handler, end_state=0):
  76.         """All states and handlers are functions which return
  77.         a state and a cargo."""
  78.         self.handlers.append(handler)
  79.         if end_state:
  80.             self.endStates.append(handler)
  81.     def set_start(self, handler):
  82.         """Sets the starting handler function."""
  83.         self.startState = handler
  84.  
  85.  
  86.     def run(self, cargo=None):
  87.         if not self.startState:
  88.             raise InitializationError,\
  89.                   "must call .set_start() before .run()"
  90.         if not self.endStates:
  91.             raise InitializationError, \
  92.                   "at least one state must be an end_state"
  93.         handler = self.startState
  94.         while 1:
  95.             (newState, cargo) = handler(cargo)
  96.             #print cargo
  97.             if newState in self.endStates:
  98.                 return newState(cargo)
  99.                 #break
  100.             elif newState not in self.handlers:
  101.                 raise RuntimeError, "Invalid target %s" % newState
  102.             else:
  103.                 handler = newState
  104.  
  105. def get_name(data):
  106.     """Get the name of an object from its object data.
  107.  
  108.     Returns a pair of (data_item, name) where data_item is the list entry where the name was found
  109.     (the data_item can be used to remove the entry from the object data).  Be sure to check
  110.     name not None before using the returned values!
  111.     """
  112.     value = None
  113.     for item in data:
  114.         if item[0] == 2:
  115.             value = item[1]
  116.             break
  117.     return item, value
  118.  
  119. def get_layer(data):
  120.     """Expects object data as input.
  121.  
  122.     Returns (entry, layer_name) where entry is the data item that provided the layer name.
  123.     """
  124.     value = None
  125.     for item in data:
  126.         if item[0] == 8:
  127.             value = item[1]
  128.             break
  129.     return item, value
  130.  
  131.  
  132. def convert(code, value):
  133.     """Convert a string to the correct Python type based on its dxf code.
  134.     code types:
  135.         ints = 60-79, 170-179, 270-289, 370-389, 400-409, 1060-1070
  136.         longs = 90-99, 420-429, 440-459, 1071
  137.         floats = 10-39, 40-59, 110-139, 140-149, 210-239, 460-469, 1010-1059
  138.         hex = 105, 310-379, 390-399
  139.         strings = 0-9, 100, 102, 300-309, 410-419, 430-439, 470-479, 999, 1000-1009
  140.     """
  141.     if 59 < code < 80 or 169 < code < 180 or 269 < code < 290 or 369 < code < 390 or 399 < code < 410 or 1059 < code < 1071:
  142.         value = int(float(value))
  143.     elif 89 < code < 100 or 419 < code < 430 or 439 < code < 460 or code == 1071:
  144.         value = long(float(value))
  145.     elif 9 < code < 60 or 109 < code < 150 or 209 < code < 240 or 459 < code < 470 or 1009 < code < 1060:
  146.         value = float(value)
  147.     elif code == 105 or 309 < code < 380 or 389 < code < 400:
  148.         value = int(value, 16) # should be left as string?
  149.     else: # it's already a string so do nothing
  150.         pass
  151.     return value
  152.  
  153.  
  154. def findObject(infile, kind=''):
  155.     """Finds the next occurance of an object."""
  156.     obj = False
  157.     while 1:
  158.         line = infile.readline()
  159.         if not line: # readline returns '' at eof
  160.             return False
  161.         if not obj: # We're still looking for our object code
  162.             if line.lower().strip() == '0':
  163.                 obj = True # found it
  164.         else: # we are in an object definition
  165.             if kind: # if we're looking for a particular kind
  166.                 if line.lower().strip() == kind:
  167.                     obj = Object(line.lower().strip())
  168.                     break
  169.             else: # otherwise take anything non-numeric
  170.                 if line.lower().strip() not in string.digits:
  171.                     obj = Object(line.lower().strip())
  172.                     break
  173.             obj = False # whether we found one or not it's time to start over
  174.     return obj
  175.  
  176. def handleObject(infile):
  177.     """Add data to an object until end of object is found."""
  178.     line = infile.readline()
  179.     if line.lower().strip() == 'section':
  180.         return 'section' # this would be a problem
  181.     elif line.lower().strip() == 'endsec':
  182.         return 'endsec' # this means we are done with a section
  183.     else: # add data to the object until we find a new object
  184.         obj = Object(line.lower().strip())
  185.         obj.name = obj.type
  186.         done = False
  187.         data = []
  188.         while not done:
  189.             line = infile.readline()
  190.             if not data:
  191.                 if line.lower().strip() == '0':
  192.                     #we've found an object, time to return
  193.                     return obj
  194.                 else:
  195.                     # first part is always an int
  196.                     data.append(int(line.lower().strip()))
  197.             else:
  198.                 data.append(convert(data[0], line.strip()))
  199.                 obj.data.append(data)
  200.                 data = []
  201.  
  202. def handleTable(table, infile):
  203.     """Special handler for dealing with nested table objects."""
  204.     item, name = get_name(table.data)
  205.     if name: # We should always find a name
  206.         table.data.remove(item)
  207.         table.name = name.lower()
  208.     # This next bit is from handleObject
  209.     # handleObject should be generalized to work with any section like object
  210.     while 1:
  211.         obj = handleObject(infile)
  212.         if obj.type == 'table':
  213.             print "Warning: previous table not closed!"
  214.             return table
  215.         elif obj.type == 'endtab':
  216.             return table # this means we are done with the table
  217.         else: # add objects to the table until one of the above is found
  218.             table.data.append(obj)
  219.  
  220.  
  221.  
  222.  
  223. def handleBlock(block, infile):
  224.     """Special handler for dealing with nested table objects."""
  225.     item, name = get_name(block.data)
  226.     if name: # We should always find a name
  227.         block.data.remove(item)
  228.         block.name = name
  229.     # This next bit is from handleObject
  230.     # handleObject should be generalized to work with any section like object
  231.     while 1:
  232.         obj = handleObject(infile)
  233.         if obj.type == 'block':
  234.             print "Warning: previous block not closed!"
  235.             return block
  236.         elif obj.type == 'endblk':
  237.             return block # this means we are done with the table
  238.         else: # add objects to the table until one of the above is found
  239.             block.data.append(obj)
  240.  
  241.  
  242.  
  243.  
  244. """These are the states/functions used in the State Machine.
  245. states:
  246.  start - find first section
  247.  start_section - add data, find first object
  248.    object - add obj-data, watch for next obj (called directly by start_section)
  249.  end_section - look for next section or eof
  250.  end - return results
  251. """
  252.  
  253. def start(cargo):
  254.     """Expects the infile as cargo, initializes the cargo."""
  255.     #print "Entering start state!"
  256.     infile = cargo
  257.     drawing = Object('drawing')
  258.     section = findObject(infile, 'section')
  259.     if section:
  260.         return start_section, (infile, drawing, section)
  261.     else:
  262.         return error, (infile, "Failed to find any sections!")
  263.  
  264. def start_section(cargo):
  265.     """Expects [infile, drawing, section] as cargo, builds a nested section object."""
  266.     #print "Entering start_section state!"
  267.     infile = cargo[0]
  268.     drawing = cargo[1]
  269.     section = cargo[2]
  270.     # read each line, if it is an object declaration go to object mode
  271.     # otherwise create a [index, data] pair and add it to the sections data.
  272.     done = False
  273.     data = []
  274.     while not done:
  275.         line = infile.readline()
  276.  
  277.         if not data: # if we haven't found a dxf code yet
  278.             if line.lower().strip() == '0':
  279.                 # we've found an object
  280.                 while 1: # no way out unless we find an end section or a new section
  281.                     obj = handleObject(infile)
  282.                     if obj == 'section': # shouldn't happen
  283.                         print "Warning: failed to close previous section!"
  284.                         return end_section, (infile, drawing)
  285.                     elif obj == 'endsec': # This section is over, look for the next
  286.                         drawing.data.append(section)
  287.                         return end_section, (infile, drawing)
  288.                     elif obj.type == 'table': # tables are collections of data
  289.                         obj = handleTable(obj, infile) # we need to find all there contents
  290.                         section.data.append(obj) # before moving on
  291.                     elif obj.type == 'block': # the same is true of blocks
  292.                         obj = handleBlock(obj, infile) # we need to find all there contents
  293.                         section.data.append(obj) # before moving on
  294.                     else: # found another sub-object
  295.                         section.data.append(obj)
  296.             else:
  297.                 data.append(int(line.lower().strip()))
  298.         else: # we have our code, now we just need to convert the data and add it to our list.
  299.             data.append(convert(data[0], line.strip()))
  300.             section.data.append(data)
  301.             data = []
  302. def end_section(cargo):
  303.     """Expects (infile, drawing) as cargo, searches for next section."""
  304.     #print "Entering end_section state!"
  305.     infile = cargo[0]
  306.     drawing = cargo[1]
  307.     section = findObject(infile, 'section')
  308.     if section:
  309.         return start_section, (infile, drawing, section)
  310.     else:
  311.         return end, (infile, drawing)
  312.  
  313. def end(cargo):
  314.     """Expects (infile, drawing) as cargo, called when eof has been reached."""
  315.     #print "Entering end state!"
  316.     infile = cargo[0]
  317.     drawing = cargo[1]
  318.     #infile.close()
  319.     return drawing
  320.  
  321. def error(cargo):
  322.     """Expects a (infile, string) as cargo, called when there is an error during processing."""
  323.     #print "Entering error state!"
  324.     infile = cargo[0]
  325.     err = cargo[1]
  326.     infile.close()
  327.     print "There has been an error:"
  328.     print err
  329.     return False
  330.  
  331. def readDXF(filename, objectify):
  332.     """Given a file name try to read it as a dxf file.
  333.  
  334.     Output is an object with the following structure
  335.     drawing
  336.         header
  337.             header data
  338.         classes
  339.             class data
  340.         tables
  341.             table data
  342.         blocks
  343.             block data
  344.         entities
  345.             entity data
  346.         objects
  347.             object data
  348.     where foo data is a list of sub-objects.  True object data
  349.     is of the form [code, data].
  350. """
  351.     infile = open(filename)
  352.  
  353.     sm = StateMachine()
  354.     sm.add_state(error, True)
  355.     sm.add_state(end, True)
  356.     sm.add_state(start_section)
  357.     sm.add_state(end_section)
  358.     sm.add_state(start)
  359.     sm.set_start(start)
  360.     try:
  361.         drawing = sm.run(infile)
  362.         if drawing:
  363.             drawing.name = filename
  364.             for obj in drawing.data:
  365.                 item, name = get_name(obj.data)
  366.                 if name:
  367.                     obj.data.remove(item)
  368.                     obj.name = name.lower()
  369.                     setattr(drawing, name.lower(), obj)
  370.                     # Call the objectify function to cast
  371.                     # raw objects into the right types of object
  372.                     obj.data = objectify(obj.data)
  373.                 #print obj.name
  374.     finally:
  375.         infile.close()
  376.     return drawing
  377. if __name__ == "__main__":
  378.     filename = r".\examples\block-test.dxf"
  379.     drawing = readDXF(filename)
  380.     for item in drawing.entities.data:
  381.         print item
  382.